package com.jiao.table.jdbc;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.sql.SqlUtil;
import com.jiao.comm.exception.CacheSysException;
import com.jiao.comm.exception.ResultMappingException;
import com.jiao.comm.utils.JDBCUtils;
import com.jiao.comm.utils.LogUtil;
import com.jiao.comm.utils.SpringUtils;
import com.jiao.table.config.CacheDataLoad;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

/**
 * @Description
 * @Author Vincent.jiao
 * @Date 2022/6/6 11:37
 */
@Service
public class TableMapperService {
    private static TableMapperService tableMapperService;

    @Autowired
    DataSource dataSource;

    public static TableMapperService getInstance(){
        if(tableMapperService == null) {
            tableMapperService = SpringUtils.getBean("tableMapperService");
        }

        return tableMapperService;
    }

    private Connection getConn() {
        try {
            return dataSource.getConnection();
        } catch (SQLException e) {
            throw new CacheSysException(e.getMessage(), e);
        }
    }

    public <T> List<T> selectAll(String tableName, Class<T> clazz){
        String sql = SQLBuilder.getSelectAllSql(tableName);
        return select(sql, clazz);
    }

    public <T> T selectByPrimaryKey(String tableName, Long id, Class clazz) {
        String sql = SQLBuilder.getSelectByPrimaryKeySql(tableName);
        if(StrUtil.isEmpty(sql)) {
            return null;
        }

        List<T> datas = select(sql, clazz, id);
        if(CollectionUtil.isEmpty(datas)) {
            return null;
        }

        if(datas.size() > 1) {
            throw new ResultMappingException("查询到不止一条数据, sql:" + sql+", id:" + id);
        }

        return datas.get(0);
    }

    private <T> List<T> select(String sql, Class clazz, Object... params) {
        List<T> datas = null;
        try(
                Connection conn = getConn();
                ResultSet rs  = JDBCUtils.select(conn, sql, params);
        ) {

            datas = mapperResult(rs, clazz);
        } catch (SQLException | InstantiationException | IllegalAccessException | NoSuchFieldException e) {
            throw new CacheSysException(e.getMessage(), e);
        }

        return datas;
    }

    public int deleteByPrimaryKey(String tableName, Long id) {
        String sql = SQLBuilder.getDeleteByPrimaryKeySql(tableName);
        if(StrUtil.isEmpty(sql)) {
            return -1;
        }

        try {
            return JDBCUtils.execuCUD(getConn(), sql, id);
        } catch (SQLException e) {
            throw new CacheSysException(e.getMessage(), e);
        }
    }

    private <T> List<T> mapperResult(ResultSet rs, Class<T> clazz) throws SQLException, IllegalAccessException, InstantiationException, NoSuchFieldException {
        ResultSetMetaData metaData = rs.getMetaData();
        List<T> datas = new LinkedList<>();

        while (rs.next()) {
            T t = clazz.newInstance();

            for (int i = 1; i <= metaData.getColumnCount(); i++) {
                String colName = metaData.getColumnName(i);
                Field field = clazz.getDeclaredField(colName);
                Class fieldType = field.getType();
                field.setAccessible(true);

                if (Integer.class.equals(fieldType)) {
                    field.set(t, rs.getInt(colName));
                } else if (Date.class.equals(fieldType)) {
                    field.set(t, rs.getTimestamp(colName));
                } else if (Timestamp.class.equals(fieldType)) {
                    field.set(t, rs.getTimestamp(colName));
                } else if (Long.class.equals(fieldType)) {
                    field.set(t, rs.getLong(colName));
                } else if (Short.class.equals(fieldType)) {
                    field.set(t, rs.getShort(colName));
                } else if (Byte.class.equals(fieldType)) {
                    field.set(t, rs.getByte(colName));
                } else if (Character.class.equals(fieldType)) {
                    String val = rs.getString(colName);
                    field.set(t, StrUtil.isEmpty(val) ? null : val.charAt(0));
                } else if (String.class.equals(fieldType)) {
                    int sqlTypeNum = metaData.getColumnType(i);
                    if (sqlTypeNum == 2005) {        // CLOB
                        field.set(t, SqlUtil.clobToStr(rs.getClob(colName)));
                    } else {
                        field.set(t, rs.getString(colName));
                    }

                } else if (Float.class.equals(fieldType)) {
                    field.set(t, rs.getFloat(colName));
                } else if (Double.class.equals(fieldType)) {
                    field.set(t, rs.getDouble(colName));
                } else if (Boolean.class.equals(fieldType)) {
                    field.set(t, rs.getBoolean(colName));
                } else if (Clob.class.equals(fieldType)) {
                    field.set(t, rs.getClob(colName));
                } else if (Blob.class.equals(fieldType)) {
                    field.set(t, rs.getBlob(colName));
                }
            }

            datas.add(t);
        }

        return datas;
    }
}
